/*
 * @Author: AiLjx
 * @Date: 2022-11-29 20:55:16
 * @LastEditors: AiLjx
 * @LastEditTime: 2022-11-30 17:32:04
 */

// 地图
class GameMap {
    constructor(row) {
        this.con = document.querySelector(".container");
        // 行数
        this.row = row;
        // 单元格大小
        this.cellSize = Math.round(this.con.clientHeight / this.row);
        // 列数
        this.col = Math.round(this.con.clientWidth / this.cellSize);
    }
}

// 蛇
class Snake {
    constructor(map, food) {
        this.map = map;
        this.food = food;
        this.timer = null;
        this.score = document.querySelector(".score span");
        this.init();
    }
    // 初始渲染
    init() {
        const doms = document.createDocumentFragment();
        snakeConfig.form.forEach((item) => {
            // = 运算符返回 = 右边的内容
            doms.appendChild((item.dom = this.generateCell(item)));
        });
        this.map.con.appendChild(doms);
    }

    /**
     * 开始运动
     * @return {*}
     */
    action() {
        if (this.timer) {
            clearInterval(this.timer);
        }
        this.timer = setInterval(
            this.move.bind(this, this.food),
            snakeConfig.speed
        );
    }
    /**
     * 暂停运动
     * @return {*}
     */
    suspend() {
        clearInterval(this.timer);
        this.timer = null;
    }

    /**
     * 生成单元格
     * @param {*} cellObj 单元格对象（蛇的身体部分）
     * @return {*}
     */
    generateCell(cellObj) {
        let div = document.createElement("div");
        div.className = "cell";
        div.style.width = this.map.cellSize + "px";
        div.style.height = this.map.cellSize + "px";
        div.style.top = cellObj.y * this.map.cellSize + "px";
        div.style.left = cellObj.x * this.map.cellSize + "px";
        // 如果是蛇头
        if (cellObj.isHead) {
            div.textContent = "®️";
        } else {
            div.textContent = Math.random().toString(36)[2];
        }

        return div;
    }

    /**
     * 移动
     * @param {*} foodInfo 食物的信息
     * @return {*}
     */
    move(foodInfo) {
        let oldHead = snakeConfig.form[snakeConfig.form.length - 1];
        let newHead = {
            x: oldHead.x + snakeConfig.direction.x,
            y: oldHead.y + snakeConfig.direction.y,
            isHead: true,
            dom: null,
        };

        if (
            newHead.x > this.map.col - 1 ||
            newHead.x < 0 ||
            newHead.y > this.map.row - 1 ||
            newHead.y < 0 ||
            snakeConfig.form.find((s) => s.x === newHead.x && s.y === newHead.y)
        ) {
            // 暂停运动
            this.suspend();
            snakeConfig.form.forEach((item) => item.dom.remove());
            snakeConfig = {
                step: 5,
                speed: 200,
                direction: operation.right,
                form: [
                    { x: 0, y: 0, dom: null },
                    { x: 1, y: 0, dom: null },
                    { x: 2, y: 0, dom: null },
                    { x: 3, y: 0, dom: null, isHead: true },
                ],
            };

            if (confirm("你失败了，是否重新开始！")) {
                // 重置分数
                this.score.textContent = 0;
                this.action();
            } else {
                document.querySelector(".action").style.display = "block";
            }
            this.init();
            return;
        }
        // 设置老的蛇头
        oldHead.isHead = false;
        oldHead.dom.textContent = Math.random().toString(36)[2];

        // 设置新的蛇头
        newHead.dom = this.generateCell(newHead);
        // 添加新蛇头
        snakeConfig.form.push(newHead);
        this.map.con.appendChild(newHead.dom);

        if (!foodInfo || newHead.x !== foodInfo.x || newHead.y !== foodInfo.y) {
            // 删除蛇尾
            snakeConfig.form[0].dom.remove();
            snakeConfig.form.shift();
        } else {
            // 更新分数
            this.score.textContent = Number(this.score.textContent) + 1;
            // 重新渲染食物
            foodInfo.renderFoodDiv();
            // 减少定时器延迟/增加速度
            snakeConfig.speed -= snakeConfig.step;
            // 重新开始运动
            this.action();
        }
    }
}

// 食物
class Food {
    constructor(map) {
        this.map = map;
        this.foodDiv = null;
        this.x = 0;
        this.y = 0;
        this.renderFoodDiv();
    }
    // 渲染食物
    renderFoodDiv() {
        if (!this.foodDiv) {
            this.foodDiv = document.createElement("div");
            this.foodDiv.className = "cell";
            this.foodDiv.textContent = "🍅";
            this.foodDiv.style.width = this.map.cellSize + "px";
            this.foodDiv.style.height = this.map.cellSize + "px";
            this.map.con.appendChild(this.foodDiv);
        }

        while (true) {
            this.x = Math.floor(Math.random() * this.map.col);
            this.y = Math.floor(Math.random() * this.map.row);
            if (
                !snakeConfig.form.find(
                    (item) => item.x === this.x && item.y === this.y
                )
            ) {
                this.foodDiv.style.top = this.y * this.map.cellSize + "px";
                this.foodDiv.style.left = this.x * this.map.cellSize + "px";
                break;
            }
        }
    }
}

class UI {
    constructor() {
        this.isAction = false;
        this.map = new GameMap(30);
        this.food = new Food(this.map);
        this.snake = new Snake(this.map, this.food);
        this.con = document.querySelector(".container");
        this.actionBtn = document.querySelector(".action");
        this.score = document.querySelector(".score span");
        this.btn = document.querySelector(".btn");
        this.listenEvent();
    }
    // 监听各种事件
    listenEvent() {
        // 按键与处理函数的映射
        const operObj = [
            [
                ["ArrowUp", "w"],
                (dire) => {
                    if (dire !== "bottom") {
                        snakeConfig.direction = operation["top"];
                    }
                },
            ],
            [
                ["ArrowDown", "s"],
                (dire) => {
                    if (dire !== "top") {
                        console.log("走！");
                        snakeConfig.direction = operation["bottom"];
                        console.log(snakeConfig.direction);
                    }
                },
            ],
            [
                ["ArrowRight", "d"],
                (dire) => {
                    if (dire !== "left") {
                        snakeConfig.direction = operation["right"];
                    }
                },
            ],
            [
                ["ArrowLeft", "a"],
                (dire) => {
                    if (dire !== "right") {
                        snakeConfig.direction = operation["left"];
                    }
                },
            ],
        ];
        // 键盘事件
        window.addEventListener("keydown", (e) => {
            // 响应空格
            if (e.code === "Space") {
                this.snake.timer ? this.suspend() : this.action();
                return;
            }
            // 当timer存在，即蛇正在运动时再继续向下执行
            if (!this.snake.timer) return;

            const dire = snakeConfig.direction.direction;
            const opItem = operObj.find((item) => item[0].includes(e.key));
            opItem && opItem[1](dire);
        });
        // 点击事件
        this.btn.addEventListener("click", (e) => {
            if (!this.snake.timer) return;
            const dire = snakeConfig.direction.direction;

            const opItem = operObj.find((item) =>
                item[0].includes(e.target.textContent.toLowerCase())
            );
            console.log(opItem[1]);
            opItem && opItem[1](dire);
        });

        this.con.addEventListener("click", (e) => {
            if (e.target.className === "action") {
                this.action();
            } else {
                this.suspend();
            }
        });
    }
    // 开始游戏
    action() {
        this.isAction = true;
        this.snake.action();
        this.actionBtn.style.display = "none";
    }
    // 暂停游戏
    suspend() {
        this.isAction = false;
        this.snake.suspend();
        this.actionBtn.style.display = "block";
    }
}

new UI();
